package com.zmc.lostfound.system.config;

import cn.hutool.core.util.BooleanUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author zmc
 * @description 自定义线程池
 */
@Configuration
public class MyThreadPoolConfig {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
        /* 线程池7大参数
         * corePoolSize: 核心线程数。在线程池创建好之后立即准备就绪的空闲线程数量，等待着接受异步任务去执行。
         *               只要线程池不销毁，核心线程就一直存在，除非设置allowCoreThreadTimeOut为true。
         *               allowCoreThreadTimeOut默认为false，核心线程即使在空闲时也会保持活动状态，不会销毁；
         *               如果将allowCoreThreadTimeOut设置为true，那么核心线程将使用keepAliveTime来超时销毁。
         *
         * maximumPoolSize: 最大线程数。即线程池中的线程数上限，用于控制线程资源。
         *
         * keepAliveTime: 线程存活时间。如果当前线程数大于核心线程数，那么空闲时间大于keepAliveTime的线程就会自动销毁，
         *                直到当前线程数 = 核心线程数。如果allowCoreThreadTimeOut设置为true，那么核心线程空闲超时也会自动销毁。
         *
         * unit: keepAliveTime的时间单位。
         *
         * workQueue: 阻塞队列。如果异步任务数量大于核心线程数，那么就会将多出的异步任务放入阻塞队列中，等待线程池出现空闲线程后再去执行。
         *
         * threadFactor: 线程创建工厂。
         *
         * handle: 拒绝策略。拒绝执行多出的异步任务。
         *
         * 线程池工作流程如下:
         * 1. 线程池创建后，立即准备好corePoolSize数量的核心线程，等待接受异步任务去执行。
         * 2. 如果核心线程都在执行任务(无空闲核心线程)，就将再进来的异步任务放入阻塞队列中，等待出现空闲的核心线程再去队列中取出任务并执行。
         * 3. 如果阻塞队列满了，线程池会开启新的线程去执行任务，但线程数量最大只能开到maximumPoolSize指定的数量。
         * 4. 如果当前线程数=最大线程数，无空闲线程，阻塞队列也满了，那么再进来的新任务就会按照拒绝策略来拒绝执行
         * 5. 如果当前线程数大于核心线程数，那么空闲时间大于keepAliveTime的线程就会自动销毁，直到当前线程数 = 核心线程数。
         *
         * 补充:
         * 1. new LinkedBlockingQueue<>()的队列长度默认是Integer的最大值(2^31-1)，因此使用时要手动设置合理长度，避免内存不足
         * 2.1 new ThreadPoolExecutor.AbortPolicy(): 拒绝策略，将新进入线程池的异步任务丢弃，并抛出异常
         * 2.2 new ThreadPoolExecutor.DiscardPolicy(): 拒绝策略，将新进入线程池的异步任务丢弃，但不会抛出异常
         * 2.3 new ThreadPoolExecutor.DiscardOldestPolicy(): 拒绝策略，将队列中最早的(队首)异步任务丢弃，但不会抛出异常
         * 2.4 new ThreadPoolExecutor.CallerRunsPolicy(): 拒绝策略，同步(串行)执行新进入线程池的任务，不会丢弃。
         *
         * 示例: 线程池: corePoolSize = 7, maximumPoolSize = 20, queueLength = 50; 100个并发异步任务怎么执行?
         * 答: 首先7个任务会立即执行，然后50个任务会进入阻塞队列，阻塞队列满了，然后最多再开启13个线程执行任务，最后剩下30个任务就会按照拒绝策略丢弃。
         * 如果这30个任务不想丢弃，那么就可以使用new ThreadPoolExecutor.CallerRunsPolicy()来同步(串行)执行任务。
         * */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                pool.getCorePoolSize(),
                pool.getMaximumPoolSize(),
                pool.getKeepAliveTime(),
                pool.getTimeUnit(),
                new LinkedBlockingQueue<>(pool.getWorkQueueLength()),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        if (BooleanUtil.isTrue(pool.getAllowCoreThreadTimeOut())) {
            threadPoolExecutor.allowCoreThreadTimeOut(true);
        }
        return threadPoolExecutor;
    }

}
